-- Copyright (c) 2010, Pavel Kovar
-- All rights reserved.
--
---------------------------------------------------------------------------------------
-- This file is a part of the Witch Navigator project

-- PCIe TPL packet reception and processing
-- Implemented
--   * Single DW memory write transaction
--   * Single DW memory read request transaction
--   * 32 and 64 bits address

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

ENTITY PCIeRX IS	
PORT (
  signal clk : 				IN std_logic;
  signal rst_n : 				IN std_logic;
  signal trn_rd :				IN std_logic_vector(31 downto 0);
  signal trn_rsof_n : 		IN std_logic;
  signal trn_reof_n : 		IN std_logic;    
  signal trn_rsrc_rdy_n :	IN std_logic; 
  signal trn_rsrc_dsc_n	: 	IN std_logic; 
  signal trn_rbar_hit_n	:	IN std_logic_vector(6 downto 0); 
  signal trn_rdst_rdy_n :	OUT std_logic;
  signal req_compl_o :	   OUT std_logic;
  signal compl_done_i :		IN std_logic;   

  signal req_tc_o :			OUT std_logic_vector(2 downto 0); -- Memory Read TC
  signal req_td_o	:			OUT std_logic;						    -- Memory Read TD
  signal req_ep_o :	      OUT std_logic;							 -- Memory Read EP
  signal req_attr_o :	   OUT std_logic_vector(1 downto 0); -- Memory Read Attribute
  signal req_len_o :		   OUT std_logic_vector(9 downto 0); -- Memory Read Length (1DW)
  signal req_rid_o :       OUT std_logic_vector(15 downto 0);-- Memory Read Requestor ID
  signal req_tag_o :			OUT std_logic_vector(7 downto 0); -- Memory Read Tag
  signal req_be_o :			OUT std_logic_vector(7 downto 0); -- Memory Read Byte Enables
  signal req_addr_o :    	OUT std_logic_vector(19 downto 0);-- Memory Read Address
  signal completer_id_i :	IN std_logic_vector(15 downto 0);
  signal wr_addr_o :			OUT std_logic_vector(19 downto 0);-- Memory Write Address
  signal wr_data_o :       OUT std_logic_vector(31 downto 0);-- Memory Write Data
  signal wr_en_o :         OUT std_logic_vector(3 downto 0));-- Memory Write Enable
end PCIeRX;

architecture Behavioral of PCIeRX is

  -- TLP Header format/type values
  constant PIO_32_RX_MEM_RD32_FMT_TYPE : std_logic_vector(6 downto 0) := "0000000";
  constant PIO_32_RX_MEM_WR32_FMT_TYPE : std_logic_vector(6 downto 0) := "1000000";
  constant PIO_32_RX_MEM_RD64_FMT_TYPE : std_logic_vector(6 downto 0) := "0100000";
  constant PIO_32_RX_MEM_WR64_FMT_TYPE : std_logic_vector(6 downto 0) := "1100000";

  -- State values
  constant PIO_32_RX_RST_STATE    :  std_logic_vector(3 downto 0) := "0000";
  constant PIO_32_RX_MEM_RD32_DW1 :  std_logic_vector(3 downto 0) := "0001";  
  constant PIO_32_RX_MEM_RD32_DW2 :  std_logic_vector(3 downto 0) := "0010";  
  constant PIO_32_RX_MEM_RD64_DW1 :  std_logic_vector(3 downto 0) := "0011";  
  constant PIO_32_RX_MEM_RD64_DW2 :  std_logic_vector(3 downto 0) := "0100";  

  constant PIO_32_RX_MEM_WR32_DW1 :  std_logic_vector(3 downto 0) := "0101";  
  constant PIO_32_RX_MEM_WR32_DW2 :  std_logic_vector(3 downto 0) := "0110";  
  constant PIO_32_RX_MEM_WR32_DW3 :  std_logic_vector(3 downto 0) := "0111";  
  constant PIO_32_RX_MEM_WR64_DW1 :  std_logic_vector(3 downto 0) := "1000";  
  constant PIO_32_RX_MEM_WR64_DW2 :  std_logic_vector(3 downto 0) := "1001";  
  constant PIO_32_RX_WAIT_STATE   :  std_logic_vector(3 downto 0) := "1010";  
  
  -- Local Registers

  signal state : std_logic_vector(3 downto 0);
  signal req_be : std_logic_vector(7 downto 0);  -- Memory Read Byte Enables     -- Memory Read Byte Enables	 
  signal tlp_type : std_logic_vector(6 downto 0);

  signal trn_rdst_rdy_n_pom : std_logic;
  signal req_len_o_pom : std_logic_vector(9 downto 0); -- Length in MRd32/64

begin

trn_rdst_rdy_n <= trn_rdst_rdy_n_pom;
req_len_o <= req_len_o_pom;
process(clk)
  begin
  if clk'event and clk = '1' then 
    if rst_n = '0' then
        trn_rdst_rdy_n_pom    <= '0';
        req_compl_o           <= '0';
        req_tc_o              <= "000";
        req_td_o              <= '0';
        req_ep_o              <= '0';
        req_attr_o            <= "00";
        req_len_o_pom         <= "0000000000";
        req_rid_o             <= "0000000000000000";
        req_tag_o             <= "00000000";
        req_be                <= "00000000";
        req_addr_o            <= "00000000000000000000";
        wr_addr_o             <= "00000000000000000000";
        wr_data_o             <= "00000000000000000000000000000000";
        wr_en_o               <= "0000";
        state                 <= PIO_32_RX_RST_STATE;
        tlp_type              <= "0000000";
    else
      wr_en_o               <= "0000";
      req_compl_o           <= '0';
      case state is
        when PIO_32_RX_RST_STATE =>
          trn_rdst_rdy_n_pom   <= '0';
          tlp_type             <= trn_rd(30 downto 24);
          req_tc_o             <= trn_rd(22 downto 20);
          req_td_o             <= trn_rd(15);
          req_ep_o             <= trn_rd(14);
          req_attr_o           <= trn_rd(13 downto 12);
          req_len_o_pom        <= trn_rd(9 downto 0);		 
			 
          if ((trn_rsof_n='0') and (trn_rsrc_rdy_n='0') and (trn_rdst_rdy_n_pom='0') and (trn_rd(9 downto 0) = "0000000001")) then
              case trn_rd(30 downto 24) is
                  when PIO_32_RX_MEM_RD32_FMT_TYPE =>
                      state        <= PIO_32_RX_MEM_RD32_DW1;
                  when PIO_32_RX_MEM_RD64_FMT_TYPE =>
                      state        <= PIO_32_RX_MEM_RD64_DW1;
                  when PIO_32_RX_MEM_WR32_FMT_TYPE =>
                      state        <= PIO_32_RX_MEM_WR32_DW1;
                  when PIO_32_RX_MEM_WR64_FMT_TYPE =>
                      state        <= PIO_32_RX_MEM_WR64_DW1;
                  when others =>
                      state          <= PIO_32_RX_RST_STATE;
              end case;
          else 
              state <= PIO_32_RX_RST_STATE;
          end if;
        
        when PIO_32_RX_MEM_RD32_DW1 =>
		  
          if (trn_rsrc_rdy_n='0') and (trn_rdst_rdy_n_pom='0') then
            req_rid_o          <= trn_rd(31 downto 16);
            req_tag_o          <= trn_rd(15 downto 8);
            req_be             <= trn_rd(7 downto 0);
            state              <= PIO_32_RX_MEM_RD32_DW2;
          else 
            state              <= PIO_32_RX_MEM_RD32_DW1;
          end if;
      when PIO_32_RX_MEM_RD32_DW2 =>
          if (trn_rsrc_rdy_n='0') and (trn_rdst_rdy_n_pom='0') then
              req_addr_o         <= trn_rd(21 downto 2);
              req_compl_o        <= '1';
              trn_rdst_rdy_n_pom <= '1';
              state              <= PIO_32_RX_WAIT_STATE;
          else
              state              <= PIO_32_RX_MEM_RD32_DW2;
          end if;
        when PIO_32_RX_MEM_WR32_DW1 =>
            if (trn_rsrc_rdy_n='0') and (trn_rdst_rdy_n_pom='0') then
                req_be             <= trn_rd(7 downto 0);
                state              <= PIO_32_RX_MEM_WR32_DW2;
            else
                state              <= PIO_32_RX_MEM_WR32_DW1;
            end if;
        when PIO_32_RX_MEM_WR32_DW2 =>
            if (trn_rsrc_rdy_n='0') and (trn_rdst_rdy_n_pom='0') then
                wr_addr_o          <= trn_rd(21 downto 2);
                state              <= PIO_32_RX_MEM_WR32_DW3;
            else
                state              <= PIO_32_RX_MEM_WR32_DW2;
            end if;
        
        -- write of the DW to the memory
        when PIO_32_RX_MEM_WR32_DW3 =>	
            if (trn_rsrc_rdy_n='0') and (trn_rdst_rdy_n_pom='0') then
                -- Bytes order commutation
                wr_data_o(7 downto 0)   <= trn_rd(31 downto 24);
                wr_data_o(15 downto 8)  <= trn_rd(23 downto 16);
                wr_data_o(23 downto 16) <= trn_rd(15 downto 8);
                wr_data_o(31 downto 24) <= trn_rd(7 downto 0);
                wr_en_o                 <= req_be(3 downto 0);
                req_compl_o             <= '0';
                trn_rdst_rdy_n_pom      <= '1';				
                state                   <= PIO_32_RX_WAIT_STATE; 			
            else
                state                   <= PIO_32_RX_MEM_WR32_DW3;
            end if;
        
        when PIO_32_RX_MEM_RD64_DW1 =>
          if (trn_rsrc_rdy_n='0') and (trn_rdst_rdy_n_pom='0') then
            req_rid_o          <=  trn_rd(31 downto 16);
            req_tag_o          <=  trn_rd(15 downto 8);
            req_be             <=  trn_rd(7 downto 0);
            state              <=  PIO_32_RX_MEM_RD64_DW2;
          else
            state              <=  PIO_32_RX_MEM_RD64_DW1;
          end if;
        
		  -- Most significant part of the adress - ignored
        when PIO_32_RX_MEM_RD64_DW2 =>
          if (trn_rsrc_rdy_n='0') and (trn_rdst_rdy_n_pom='0') then
            state              <= PIO_32_RX_MEM_RD32_DW2; -- same as 32 bits RD
          else
            state              <= PIO_32_RX_MEM_RD64_DW2;
          end if;
        
        when PIO_32_RX_MEM_WR64_DW1 =>
            if (trn_rsrc_rdy_n='0') and (trn_rdst_rdy_n_pom='0') then
                req_be             <= trn_rd(7 downto 0);
                state              <= PIO_32_RX_MEM_WR64_DW2;
            else
                state              <= PIO_32_RX_MEM_WR64_DW1;
            end if;
        
		  -- Most significant part of the adress - ignored	
        when PIO_32_RX_MEM_WR64_DW2 =>
          if (trn_rsrc_rdy_n='0') and (trn_rdst_rdy_n_pom='0') then
            state              <= PIO_32_RX_MEM_WR32_DW2;
          else
            state              <= PIO_32_RX_MEM_WR64_DW2;
          end if;
        
        when PIO_32_RX_WAIT_STATE =>
          wr_en_o              <= "0000";
          req_compl_o          <= '0';

          if (tlp_type = PIO_32_RX_MEM_WR32_FMT_TYPE) then
            trn_rdst_rdy_n_pom     <= '0';
            state              <= PIO_32_RX_RST_STATE;
          elsif (tlp_type = PIO_32_RX_MEM_WR64_FMT_TYPE) then
            trn_rdst_rdy_n_pom     <= '0';
            state              <= PIO_32_RX_RST_STATE;
          elsif (tlp_type = PIO_32_RX_MEM_RD32_FMT_TYPE) and (compl_done_i='1') then
            trn_rdst_rdy_n_pom     <= '0';
            state              <= PIO_32_RX_RST_STATE;
          elsif (tlp_type = PIO_32_RX_MEM_RD64_FMT_TYPE) and (compl_done_i='1') then
            trn_rdst_rdy_n_pom     <= '0';
            state              <= PIO_32_RX_RST_STATE;
          else
            state              <= PIO_32_RX_WAIT_STATE;
          end if;
		  when others =>
		    state              <= PIO_32_RX_RST_STATE;
		end case;
    end if;
  end if;	 
  end process;
  req_be_o <= req_be;
end Behavioral;

